import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;

/**
 * SymbolTable is an implementation of ISymbolTable using a map from label to
 * address, usage, and str values.
 * 
 * 
 * @author Michael Griscom
 * @version %I%, %G%
 * @since 4/5/2012
 */
public final class SymbolTable implements ISymbolTable {

	/**
	 * Local class used to contain the information regarding a given label.
	 * 
	 * @author Michael Griscom
	 * 
	 */
	private class LabelInformation {
		/**
		 * Address corresponding to label.
		 */
		private final int address;
		/**
		 * Usage of label (for example, program name, equ, ext).
		 */
		private final String usage;
		/**
		 * String of equated value, if symbol is equated.
		 */
		private final String str;

		/**
		 * Creates a label information instance.
		 * 
		 * @param address
		 *            is the address corresponding to the label
		 * @param usage
		 *            is a string representing the usage of the label
		 * @param str
		 *            is a string representing the equated value of the label
		 */
		// [address is used to store a label's information in
		// the SymbolTable]
		// [usage is used to store a label's information in the
		// SymbolTable]
		// [str is used to store a label's information in the
		// SymbolTable]
		public LabelInformation(int address, String usage, String str) {
			this.address = address;
			this.usage = usage;
			this.str = str;
		}
	}

	/**
	 * Map from a label to the label's information, contains every entry of the
	 * symbol table.
	 */
	// [labelMap is a mapping from a label to the label's information within the
	// SymbolTable]
	private final Map<String, LabelInformation> labelMap = new HashMap<String, LabelInformation>();

	@Override
	public void print(OutputStream os) {
		// [PrintStream used for outputting the SymbolTable]
		PrintStream out = new PrintStream(os);

		// [temporary variable to hold the label being printed in the
		// SymbolTable]
		String label;

		// [temporary variable to hold the information for the label being
		// printed in the SymbolTable]
		LabelInformation info;

		out.println("Label\tAddress\tUsage\tString");

		// [entry is a temporary variable to hold the current SymbolTable entry
		// being printed]
		for (Map.Entry<String, LabelInformation> entry : labelMap.entrySet()) {
			label = entry.getKey();
			info = entry.getValue();

			out.println(label + '\t' + info.address + '\t' + info.usage + '\t'
					+ info.str);
		}

	}

	@Override
	public boolean add(String label, int address, String usage, String str) {
		// [labelDefined is a temporary boolean that indicates if the
		// SymbolTable contains the given label]
		boolean labelDefined = labelMap.containsKey(label);
		if (!labelDefined) {
			LabelInformation info = new LabelInformation(address, usage, str);
			labelMap.put(label, info);
		}
		return labelDefined;
	}

}
